home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-04-25 | 14.7 KB | 391 lines | [ TEXT/CWIE]
//================================================================================ // Greg Anderson // db+ // // Abstract base class for cursors // 17 May 1994 // 31 Dec 1994 //================================================================================ #include "AbstractRecord.h" #include "DatabaseDocument.h" #include "GroupControlObject.h" // // Needed to use templates of these classes // #include "DBRecord.h" #include "DBElement.h" #include "DBProperty.h" #include "DataRecord.h" #include "Exceptions.h" //-------------------------------------------------------------------------------- // TAbstractRecord::TAbstractRecord //-------------------------------------------------------------------------------- TAbstractRecord::TAbstractRecord(TDatabaseDocument* doc, long recordIndex) { REQUIREVALIDPOINTER(doc); fDocument = doc; fGroupControlObject = nil; fRecordIndex = recordIndex; fChangeImage = nil; this->CacheGroupControlObject(); } // TAbstractRecord::TAbstractRecord //-------------------------------------------------------------------------------- // TAbstractRecord::CacheGroupControlObject //-------------------------------------------------------------------------------- void TAbstractRecord::CacheGroupControlObject() { fGroupControlObject = fDocument->GetGroupControlObject(fRecordIndex); } // TAbstractRecord::CacheGroupControlObject //-------------------------------------------------------------------------------- // TAbstractRecord::~TAbstractRecord //-------------------------------------------------------------------------------- TAbstractRecord::~TAbstractRecord() { // // If the group control object was set to nil, // that means that this object has been removed // from its group control object, and is now // more or less unusable (this happens when a // free record is pushed onto the free stack // and the same record index is reused for something // else--the old free record cursor is left // dangling until all of its references are // removed, at which time it is deleted and its // changes are _not_ discarded or committed, because // that has already been done.) // if(this->HasGroupControlObject()) this->DiscardChanges(this->Transaction()); else ASSERT(fChangeImage == nil); } // TAbstractRecord::~TAbstractRecord //-------------------------------------------------------------------------------- // TAbstractRecord::ObjectsKeySpace //-------------------------------------------------------------------------------- Int64 TAbstractRecord::ObjectsKeySpace() const { return DBDocument()->ObjectsKeySpace(); } // TAbstractRecord::ObjectsKeySpace //-------------------------------------------------------------------------------- // TAbstractRecord::GetRecordCursor //-------------------------------------------------------------------------------- AConst<TAbstractRecord> TAbstractRecord::GetRecordCursor(long recordIndex) const { return this->DBDocument()->GetRecordCursor(recordIndex); } // TAbstractRecord::GetRecordCursor //-------------------------------------------------------------------------------- // TAbstractRecord::GetDBRecordCursor //-------------------------------------------------------------------------------- AConst<TDBRecord> TAbstractRecord::GetDBRecordCursor(long recordIndex) const { AConst<TDBRecord> dbCursor; AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex); if(cursor.Exists()) dbCursor = AConst<TDBRecord>(cursor->AbstractDBRecord()); return dbCursor; } // TAbstractRecord::GetDBRecordCursor //-------------------------------------------------------------------------------- // TAbstractRecord::GetDBElementCursor //-------------------------------------------------------------------------------- AConst<TDBElement> TAbstractRecord::GetDBElementCursor(long recordIndex) const { AConst<TDBElement> dbCursor; AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex); if(cursor.Exists()) dbCursor = AConst<TDBElement>(cursor->DBElementRecord()); return dbCursor; } // TAbstractRecord::GetDBElementCursor //-------------------------------------------------------------------------------- // TAbstractRecord::GetDBPropertyCursor //-------------------------------------------------------------------------------- AConst<TDBProperty> TAbstractRecord::GetDBPropertyCursor(long recordIndex) const { AConst<TDBProperty> dbCursor; AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex); if(cursor.Exists()) dbCursor = AConst<TDBProperty>(cursor->DBPropertyRecord()); return dbCursor; } // TAbstractRecord::GetDBPropertyCursor //-------------------------------------------------------------------------------- // TAbstractRecord::GetDataCursor //-------------------------------------------------------------------------------- AConst<TDataRecord> TAbstractRecord::GetDataCursor(long recordIndex) const { AConst<TDataRecord> dbCursor; AConst<TAbstractRecord> cursor = this->GetRecordCursor(recordIndex); if(cursor.Exists()) dbCursor = AConst<TDataRecord>(cursor->DataRecord()); return dbCursor; } // TAbstractRecord::GetDataCursor //-------------------------------------------------------------------------------- // TAbstractRecord::CommitChanges //-------------------------------------------------------------------------------- void TAbstractRecord::CommitChanges(TTransaction* transaction) { if(this->InTransaction(transaction) == false) FailErr(eNotInTransaction); // // If we have a change image and it has been touched... // if((fChangeImage != nil) && (fChangeImageUnchanged == false)) { // // Commit the changes back to the group control object // // n.b. The group control object will compare the new // data with the old to try to avoid dirtying the // entire group unnecessarily. We can avoid an // unnecessary call to memcmp if we know for certain // that the data has changed. This information // is stored in fChangeImageMayHaveChangedBack. // this->GroupControlObject()->ChangeRecordData(this->RecordIndex(), fChangeImage, (fChangeImageMayHaveChangedBack == false)); } // // Once we've committed our changes, discard // the change image that's left over and push // this record back on the free stack if it // is free. // this->DiscardChanges(transaction); } // TAbstractRecord::CommitChanges //-------------------------------------------------------------------------------- // TAbstractRecord::DiscardChanges //-------------------------------------------------------------------------------- void TAbstractRecord::DiscardChanges(TTransaction* transaction) { if(this->InTransaction(transaction) == false) FailErr(eNotInTransaction); if(fChangeImage != nil) { // // Delete the change image object // delete [] fChangeImage; fChangeImage = nil; } this->PushRecordIfFree(transaction); } // TAbstractRecord::DiscardChanges //-------------------------------------------------------------------------------- // TAbstractRecord::GetRecordData //-------------------------------------------------------------------------------- long TAbstractRecord::GetRecordData(TTransaction* transaction, long longwordNumber) const { if((fChangeImage != nil) && (this->InTransaction(transaction))) return fChangeImage[longwordNumber]; else return this->GroupControlObject()->GetRecordWord(this->RecordIndex(), longwordNumber); } // TAbstractRecord::GetRecordData //-------------------------------------------------------------------------------- // TAbstractRecord::ChangeRecordData //-------------------------------------------------------------------------------- void TAbstractRecord::ChangeRecordData(TTransaction* transaction, long longwordNumber, long newValue) { if(this->InTransaction(transaction) == false) FailErr(eNotInTransaction); // // If we don't have a change image yet, then make one // if(fChangeImage == nil) { fChangeImage = this->GroupControlObject()->MakeRecordDataCopy(this->RecordIndex()); fChangeImageUnchanged = true; fChangeImageMayHaveChangedBack = false; } // // Don't do anything at all unless the data has // actually changed. // if(fChangeImage[longwordNumber] != newValue) { // // Write the new word into the change image, // then do some tests to see if the data has // changed at all. // fChangeImage[longwordNumber] = newValue; if(newValue == this->GroupControlObject()->GetRecordWord(this->RecordIndex(), longwordNumber)) { fChangeImageMayHaveChangedBack = true; } else { fChangeImageUnchanged = false; fChangeImageMayHaveChangedBack = false; } } } // TAbstractRecord::ChangeRecordData //-------------------------------------------------------------------------------- // TAbstractRecord::WriteThroughToTransaction // // This method is called only from // TGroupControlObject::WriteThroughToTransaction; see the comment there // for information about what this is for. //-------------------------------------------------------------------------------- void TAbstractRecord::WriteThroughToTransaction(long longwordNumber, long theData, long theMask) { // // The group control object has taken care of its data; we only // need to update our change image. // if(fChangeImage != nil) { long currentWordValue = fChangeImage[longwordNumber]; fChangeImage[longwordNumber] = (currentWordValue & ~theMask) | (theData & theMask); } } // TAbstractRecord::WriteThroughToTransaction //-------------------------------------------------------------------------------- // TAbstractRecord::CompareRecordData //-------------------------------------------------------------------------------- CompareEnumeration TAbstractRecord::CompareRecordData(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes, const TAbstractDataReference& compareWith) const { const TConstDataReference recordDataReference(this->RecordDataReference(transaction, dataType, longwordNumber, numberOfBytes)); return recordDataReference.Compare(compareWith); } // TAbstractRecord::CompareRecordData //-------------------------------------------------------------------------------- // TAbstractRecord::RecordDataContains //-------------------------------------------------------------------------------- Boolean TAbstractRecord::RecordDataContains(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes, const TAbstractDataReference& compareWith) const { const TConstDataReference recordDataReference(this->RecordDataReference(transaction, dataType, longwordNumber, numberOfBytes)); return recordDataReference.Contains(compareWith); } // TAbstractRecord::RecordDataContains //-------------------------------------------------------------------------------- // TAbstractRecord::ThisRecordIsFree //-------------------------------------------------------------------------------- Boolean TAbstractRecord::ThisRecordIsFree(TTransaction* transaction) const { return ((this->GetRecordData(transaction, kFreeNodeIDByte) & kFreeRecordIDBits) == kFreeRecordIDBits); } // TAbstractRecord::ThisRecordIsFree //-------------------------------------------------------------------------------- // TAbstractRecord::RemoveReference //-------------------------------------------------------------------------------- Boolean TAbstractRecord::RemoveReference() const { Boolean noMoreReferences = TTransactionAwareObject::RemoveReference(); // // Usually, RemoveReference will not delete the item; // however, if DisposeRecordIfUnreferenced was called // it may defer deletion until the last reference // actually goes away. In that case, we will delete // the record when the last reference is released. // // Perhaps we should move this functionality down // to the base class // if((noMoreReferences == true) && (this->HasGroupControlObject() == false)) { delete (TAbstractRecord*)this; } return noMoreReferences; } // TAbstractRecord::RemoveReference //-------------------------------------------------------------------------------- // TAbstractRecord::DisposeRecordIfUnreferenced //-------------------------------------------------------------------------------- void TAbstractRecord::DisposeRecordIfUnreferenced() { // // Abstract records are only disposed of by their group control // object at the time when they are disassociated from the GCO. // We need to signal that this record is no longer useful by // setting the group control object to nil. // // Inherited::DisposeRecordIfUnreferenced will dispose of // this record if there are no references to it, but if there // ARE references to it, then the last call to RemoveReference // will delete this object. // ASSERT(fChangeImage == nil); fGroupControlObject = nil; TTransactionAwareObject::DisposeRecordIfUnreferenced(); } // TAbstractRecord::DisposeRecordIfUnreferenced //-------------------------------------------------------------------------------- // TAbstractRecord::FreeOwnedData //-------------------------------------------------------------------------------- void TAbstractRecord::FreeOwnedData(TTransaction* /*transaction*/) { } // TAbstractRecord::FreeOwnedData //-------------------------------------------------------------------------------- // TAbstractRecord::FreeThisRecord //-------------------------------------------------------------------------------- void TAbstractRecord::FreeThisRecord(TTransaction* transaction) { if(this->ThisRecordIsFree(transaction) == false) { this->FreeOwnedData(transaction); long previousFlags = this->GetRecordData(transaction, kFreeNodeIDByte); this->ChangeRecordData(transaction, kFreeNodeIDByte, previousFlags | kFreeRecordIDBits); this->ChangeRecordData(transaction, kFreeNodeLinkByte, kFreeRecordNotLinkedToTree); } } // TAbstractRecord::FreeThisRecord //-------------------------------------------------------------------------------- // TAbstractRecord::PushRecordIfFree //-------------------------------------------------------------------------------- void TAbstractRecord::PushRecordIfFree(TTransaction* transaction) { if((this->ThisRecordIsFree(transaction)) && (this->GetRecordData(transaction, kFreeNodeLinkByte) == kFreeRecordNotLinkedToTree)) { this->DBDocument()->PushFreeRecordOntoFreeList(this->RecordIndex()); } } // TAbstractRecord::PushRecordIfFree //-------------------------------------------------------------------------------- // TAbstractRecord::RecordDataReference // // Private--only used to compare data in the record //-------------------------------------------------------------------------------- const TConstDataReference TAbstractRecord::RecordDataReference(TTransaction* transaction, long dataType, long longwordNumber, long numberOfBytes) const { if((fChangeImage != nil) && (this->InTransaction(transaction))) return TConstDataReference(dataType, (char*)&fChangeImage[longwordNumber], numberOfBytes); else return this->GroupControlObject()->RecordDataReference(this->RecordIndex(), dataType, longwordNumber, numberOfBytes); } // TAbstractRecord::RecordDataReference